#include <windows.h>
#include <tchar.h>
#include "DockWindow.h"

#define MAX_DOCK_WINDOWS 64

static HWND  hDockList[MAX_DOCK_WINDOWS];
static int   nNumDockWnds = 0;

static TCHAR szDockClass[] = _T("DockWnd32");
static ATOM  aDockClass    = 0;

static HWND GetOwner(HWND hwnd)
{
	return GetWindow(hwnd, GW_OWNER);
}

static BOOL IsOwnedBy(HWND hwndMain, HWND hwnd)
{
	return (hwnd == hwndMain) || (GetOwner(hwnd) == hwndMain);
}

void TogglePopupStyle(HWND hwnd)
{
	DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
	
	if(dwStyle & WS_CHILD)
	{
		SetWindowLong(hwnd, GWL_STYLE, (dwStyle & ~WS_CHILD) | WS_POPUP);
		SetParent(hwnd, NULL);
	}
	else
	{
		SetWindowLong(hwnd, GWL_STYLE, (dwStyle & ~WS_POPUP) | WS_CHILD);
		SetParent(hwnd, GetOwner(hwnd));
	}

}

//
//	This function only returns the popups which belong
//	to the specified "main" window
//
//	hwndMain - handle to top-level owner of the popups to retrieve
//	hwndList - where to store the list of popups
//	nItems   - [in] - size of hwndList, [out] - returned no. of windows
//	fIncMain - include the main window in the returned list
//
int GetPopupList(HWND hwndMain, HWND hwndList[], int nSize, BOOL fIncMain)
{
	int i, count = 0;
	
	if(hwndList == 0) 
		return 0;

	for(i = 0; i < nNumDockWnds && i < nSize; i++)
	{
		if(IsOwnedBy(hwndMain, hDockList[i]))
			hwndList[count++] = hDockList[i];
	}

	if(fIncMain && count < nSize)
	{
		hwndList[count++] = hwndMain;
	}

	return count;
}

//
//	Dock window procedure
//
static LRESULT CALLBACK DockWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	int    i;

	switch(msg)
	{
	case WM_NCCREATE:
		
		// Add this one to list of dock-windows. 
		if(nNumDockWnds < MAX_DOCK_WINDOWS)
		{
			hDockList[nNumDockWnds++] = hwnd;
			return TRUE;
		}
		else
		{
			return FALSE;
		}

	case WM_NCDESTROY:
		
		// remove this window from the list of dock-windows
		for(i = 0; i < nNumDockWnds; i++)
		{
			if(hDockList[i] == hwnd)
			{
				for( ; i < nNumDockWnds - 1; i++)
				{
					hDockList[i] = hDockList[i+1];
				}

				nNumDockWnds--;
				break;
			}
		}

		return 0;

	case WM_NCACTIVATE:
		return HANDLE_NCACTIVATE(GetOwner(hwnd), hwnd, wParam, lParam);

	case WM_DESTROY:
		return 0;

	case WM_CLOSE:
		DestroyWindow(hwnd);
		return 0;
	}

	return DefWindowProc(hwnd, msg, wParam, lParam);
}

static ATOM InitDockWndClass()
{
	WNDCLASSEX	wndclass;

	ZeroMemory(&wndclass, sizeof(wndclass));

	//Window class for the main application parent window
	wndclass.cbSize			= sizeof(wndclass);
	wndclass.style			= 0;
	wndclass.lpfnWndProc	= DockWndProc;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= GetModuleHandle(0);
	wndclass.hIcon			= 0;
	wndclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground	= (HBRUSH)(COLOR_BTNFACE+1);
	wndclass.lpszMenuName	= 0;
	wndclass.lpszClassName	= szDockClass;
	wndclass.hIconSm		= 0;

	return RegisterClassEx(&wndclass);
}

//
//	Create a floating dock-window
//
HWND CreateDockWnd(HWND hwndParent, TCHAR szCaption[], int x, int y, int width, int height)
{
	HWND hwnd;

	if(aDockClass == 0)
		aDockClass = InitDockWndClass();

	hwnd = CreateWindowEx(
			WS_EX_TOOLWINDOW,
			szDockClass, szCaption,
			WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_CLIPCHILDREN,
			x,			
			y,			
			width,		
			height,		
			hwndParent,	
			NULL,		
			GetModuleHandle(0),	
			NULL);					

	SetWindowText(hwnd, szCaption);
	return hwnd;
}

//
//	The main window of app should call this in response to WM_ENABLE
//
//	hwndMain - handle to top-level owner window
//	hwnd     - handle to window which received message (can be same as hwndMain)
//
LRESULT HANDLE_ENABLE(HWND hwndMain, HWND hwnd, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndList[MAX_DOCK_WINDOWS+1];
	int i, nNumWnds;

	HWND hParam = (HWND)lParam;
		
	nNumWnds = GetPopupList(hwndMain, hwndList, MAX_DOCK_WINDOWS+1, FALSE);

	for(i = 0; i < nNumWnds; i++)
	{
		if(hwndList[i] != hwnd)
		{
			EnableWindow(hwndList[i], wParam);
		}
	}

	//just do the default
	return DefWindowProc(hwnd, WM_ENABLE, wParam, lParam);
}

//
//	The main window of app should call this in response to WM_NCACTIVATE
//
//	hwndMain - handle to top-level owner window
//	hwnd     - handle to window which received message (can be same as hwndMain)
//
LRESULT HANDLE_NCACTIVATE(HWND hwndMain, HWND hwnd, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndList[MAX_DOCK_WINDOWS+1];
	int i, nNumWnds;

	HWND   hParam = (HWND)lParam;

	BOOL   fKeepActive = wParam;
	BOOL   fSyncOthers = TRUE;

	nNumWnds = GetPopupList(hwndMain, hwndList, MAX_DOCK_WINDOWS+1, TRUE);

	// UNDOCUMENTED FEATURE:
	// if the other window being activated/deactivated (i.e. NOT this one)
	// is one of our popups, then go (or stay) active, otherwise.
	for(i = 0; i < nNumWnds; i++)
	{
		if(hParam == hwndList[i])
		{
			fKeepActive = TRUE;
			fSyncOthers = FALSE;
			break;
		}
	}

	// If this message was sent by the synchronise-loop (below)
	// then exit normally
	if(hParam == (HWND)-1)
	{
		return DefWindowProc(hwnd, WM_NCACTIVATE, fKeepActive, 0);
	}

	// This window is about to change (inactive/active).
	// Sync all other popups to the same state 
	if(fSyncOthers == TRUE)
	{
		for(i = 0; i < nNumWnds; i++)
		{
			//DO NOT send this message to ourselves!!!!
			if(hwndList[i] != hwnd && hwndList[i] != hParam)
				SendMessage(hwndList[i], WM_NCACTIVATE, fKeepActive, (LONG)-1);
		}
	}

	return DefWindowProc(hwnd, WM_NCACTIVATE, fKeepActive, lParam);
}